/*
 *  Mathutilis.h
 *  c_Render
 *
 *  Created by liao peterson on 11/8/09.
 *  Copyright 2009 uestc. All rights reserved.
 *
 */
#ifndef MATH_H__
#define MATH_H__

#include<math.h>
#include"Macros.h"

#define MAX(a,b) (a>b)?a:b
#define MIN(a,b) (a<b)?a:b
#define SWAPI(a,b) {a^=b;b^=a;a^=b;}
#define SWAPF(a,b) {GLfloat c=a;a=b;b=c;}
static GLboolean GreaterF(const GLfloat a, const GLfloat b)
{
	return (a>b)?1:0;
}

static GLboolean LessF(const GLfloat a, const GLfloat b)
{
	return (a<b)?1:0;
}

static GLboolean ABS_GreaterF(const GLfloat a, const GLfloat b)
{
	return (fabs(a)>fabs(b))?1:0;
}

static GLboolean ABS_LessF(const GLfloat a, const GLfloat b)
{
	return (fabs(a)<fabs(b))?1:0;
}

typedef struct _VEC3{
	GLfloat x,y,z;
	void SetZero()
	{
		x=y=z=0;
	}
	
	// Standard way for using this operator:
	// vec3 test={1,2,3};
	// *test[0] = 2;
	GLfloat* operator[](const GLint index){
		switch(index){
			case 0:
				return &x;
			case 1:
				return &y;
			case 2:
				return &z;
			default:
				break;
		}
		return NULL;
	}
	
	_VEC3 operator+(const _VEC3 &addon) const{
		_VEC3 result;
		result.x = x + addon.x;
		result.y = y + addon.y;
		result.z = z + addon.z;
		
		return result;
	}
	
	void operator+=(const _VEC3 &addon){
		x += addon.x;
		y += addon.y;
		z += addon.z;
	}
	
	_VEC3 operator-(const _VEC3 &sub) const{
		_VEC3 result;
		result.x = x - sub.x;
		result.y = y - sub.y;
		result.z = z - sub.z;
		
		return result;
	}
	
	void operator-=(const _VEC3 &addon){
		x -= addon.x;
		y -= addon.y;
		z -= addon.z;
	}
	
	GLfloat operator*(const _VEC3 &mul) const{
		return x * mul.x + y * mul.y + z * mul.z;
	}
	
	_VEC3 operator*(const GLfloat mul) const{
		_VEC3 result;
		result.x = x * mul;
		result.y = y * mul;
		result.z = z * mul;
		
		return result;
	}
	
	void operator*=(const GLfloat mul){
		x *= mul;
		y *= mul;
		z *= mul;
	}
	
	_VEC3 operator/(const GLfloat div) const{
		_VEC3 result;
		result.x = x / div;
		result.y = y / div;
		result.z = z / div;
		
		return result;
	}
	
	void operator/=(const GLfloat mul){
		if(abs(mul)<EPSILON)
			return;
		x /= mul;
		y /= mul;
		z /= mul;
	}
	
	_VEC3 operator-() const{
		_VEC3 result;
		result.x = -x;
		result.y = -y;
		result.z = -z;
		
		return result;
	}
	
	_VEC3 operator%(const _VEC3 &mul) const
	{
		_VEC3 result;
		result.x = y * mul.z - z * mul.y;
		result.y = -(x * mul.z - z * mul.x);
		result.z = x * mul.y - y * mul.x;
		
		return result;
	}
	
	_VEC3 cross(const _VEC3 &mul) const
	{
		_VEC3 result;
		result.x = y * mul.z - z * mul.y;
		result.y = -(x * mul.z - z * mul.x);
		result.z = x * mul.y - y * mul.x;
		
		return result;
	}
	
	GLfloat norm() const{
		return sqrt(x*x + y*y + z*z);
	}
	
	void normalize(){
		GLfloat n = norm();
		if(n < EPSILON/100)
			return;
		x = x/n;
		y = y/n;
		z = z/n;
	}
	
	void setValue(GLfloat nx, GLfloat ny, GLfloat nz){
		x = nx;
		y = ny;
		z = nz;
	}
	
	void setValue(_VEC3 n){
		x = n.x;
		y = n.y;
		z = n.z;
	}
}vec3;


class vec4{
public:
	GLfloat x,y,z,w;
	
	vec4(){x=0;y=0;z=0;w=1;}
	vec4(GLfloat nx, GLfloat ny, GLfloat nz){x=nx;y=ny;z=nz;w=1;}
	vec4 operator+(const vec4 &addon) const{
		vec4 result;
		result.x = x + addon.x;
		result.y = y + addon.y;
		result.z = z + addon.z;
		result.w = w + addon.w;
		
		return result;
	}
	
	vec4 operator-(const vec4 &sub) const{
		vec4 result;
		result.x = x - sub.x;
		result.y = y - sub.y;
		result.z = z - sub.z;
		result.w = w - sub.w;
		
		return result;
	}
	
	GLfloat operator*(const vec4 &mul) const{
		return x * mul.x + y * mul.y + z * mul.z + w * mul.w;
	}
};

typedef struct _BOX{
	//vec3 vBox[8];
	vec3 halfSize;
	//lineSeg edges[12];
}box;

typedef struct _LINE_SEGMENT{
	vec3 p1,p2;
}lineSeg;

typedef struct _LINE{
	vec3 dir;				// Normalized
	vec3 p;
}line;

typedef struct _PLANE{
	vec3 n;					//Normalized
	vec3 p;
}plane;

class Quat{
public:
	Quat operator+(const Quat&) const;
	Quat operator-(const Quat&) const;
	Quat operator*(const Quat&) const;
	Quat operator/(const Quat&) const;
	Quat();
	Quat(GLfloat*);
	Quat(GLfloat,GLfloat,GLfloat,GLfloat);
	Quat(vec3 &axis, GLfloat theta);
	//~Quat();
	void Conjugate();
	void Set(GLfloat*q);
	void Set(GLfloat,GLfloat,GLfloat,GLfloat);
	void Set(const vec3 &axis,GLfloat theta);

	GLfloat w,x,y,z;
};



//Matrix Defined here are all Column Major
class Matrix3x3{
public:
	GLfloat*m;
	
	Matrix3x3();
	Matrix3x3(const Matrix3x3*);
	~Matrix3x3();
	void Copy3x3(const Matrix3x3*);
	void SetIdentity();
	void SetRotY(const GLfloat);
	void SetRotZ(const GLfloat);
	void SetRotX(const GLfloat);
	void SetRotXYZ(const GLfloat,const GLfloat,const GLfloat);
	void SetRotZYX(const GLfloat,const GLfloat,const GLfloat);
	void SetRotQuat(const Quat*);
	void SetRotQuatI(const Quat*q);
	
	GLfloat det() const;
	Matrix3x3 Inverse() const;
	void setComponent(const vec3 &compOne, const vec3 &compTwo, const vec3 &compThree);
	Matrix3x3 transpose() const;
	
	Matrix3x3 operator+(const Matrix3x3&) const;
	Matrix3x3 operator-(const Matrix3x3&) const;
	Matrix3x3 operator*(const Matrix3x3&) const;
	//Matrix3x3 operator/(const Matrix3x3&) const;
	
	vec3 operator*(const vec3) const;
};

class Matrix4x4{
public:
	GLfloat*m;

	Matrix4x4();
	Matrix4x4(const Matrix4x4*);
	~Matrix4x4();
	void Copy4x4(const Matrix4x4*);
	void SetIdentity();
	void SetOrtho(const GLfloat,const GLfloat,const GLfloat,const GLfloat,const GLfloat,const GLfloat);
	void SetTranslate(const GLfloat,const GLfloat,const GLfloat);
	void SetScale(const GLfloat,const GLfloat,const GLfloat);
	void SetRotY(const GLfloat);
	void SetRotZ(const GLfloat);
	void SetRotX(const GLfloat);
	void SetRotXYZ(const GLfloat,const GLfloat,const GLfloat);
	void SetRotZYX(const GLfloat,const GLfloat,const GLfloat);
	void SetRotQuat(const Quat*);
	void SetRotQuatI(const Quat*q);
	void SetPers(const GLfloat,const GLfloat,const GLfloat);
	
	void	  operator=(const Matrix4x4&);
	Matrix4x4 operator+(const Matrix4x4&) const;
	Matrix4x4 operator-(const Matrix4x4&) const;
	Matrix4x4 operator*(const Matrix4x4&) const;
	//Matrix4x4 operator/(const Matrix4x4&) const;
	vec3 transformInverse(const vec3&) const;
	
	vec4 operator*(const vec4) const;
	vec3 operator*(const vec3) const;
};

enum COMPARISON_FLAG {
	FLAG_GREATER,
	FLAG_LESS,
	FLAG_ABS_GREATER,
	FLAG_ABS_LESS
};

class generalAlgorithm{
public:
	// Bubble sort algorithm
	void sortArrayF(GLfloat *array, GLint size, COMPARISON_FLAG flag = FLAG_GREATER);
};

#endif